unit RtcKbmUpdate;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Grids, Vcl.DBGrids,
  RzDBGrid, RzDBNav, RzButton, RzRadChk, Vcl.StdCtrls, Vcl.Buttons, RzLabel,
  RzPanel, Vcl.ExtCtrls, RzSplit, rtcInfo, rtcDB, Data.DB, kbmMemTable,
  rtcCliModule, DataOperate, Generics.Collections;

type
  TframRtcKbmUpdate = class(TFrame)
    rzspltr4: TRzSplitter;
    pnl4: TRzPanel;
    btnMtDlData: TSpeedButton;
    lblStatus: TRzLabel;
    chkValidCheck: TCheckBox;
    btnAppUpdate: TBitBtn;
    chbQAppUpdate: TRzCheckBox;
    rzspltr2: TRzSplitter;
    RzDBNavigator3: TRzDBNavigator;
    RzDBGrid3: TRzDBGrid;
    rzspltr3: TRzSplitter;
    RzDBGrid1: TRzDBGrid;
    pnl5: TRzPanel;
    btnRefreshOrder: TSpeedButton;
    RzDBNavigator1: TRzDBNavigator;
    RzDBGrid2: TRzDBGrid;
    pnl6: TRzPanel;
    btnRefreshItem: TSpeedButton;
    RzDBNavigator4: TRzDBNavigator;
    dsDetailItem: TDataSource;
    dsDetailOrder: TDataSource;
    dsMaster: TDataSource;
    kbmtbl1: TkbmMemTable;
    kbmtbl2: TkbmMemTable;
    kbmtbl3: TkbmMemTable;
    rtcdtstmntr1: TRtcDataSetMonitor;
    rtcdtstmntr2: TRtcDataSetMonitor;
    rtcdtstmntr3: TRtcDataSetMonitor;
    procedure RtcMemDataSetDataChange(Sender: TObject);
    procedure kbmtbl1AfterScroll(DataSet: TDataSet);
    procedure kbmtbl2AfterScroll(DataSet: TDataSet);
    procedure kbmtbl1BeforeDelete(DataSet: TDataSet);
    procedure kbmtbl1BeforePost(DataSet: TDataSet);
    procedure kbmtbl2BeforeDelete(DataSet: TDataSet);
    procedure kbmtbl2BeforePost(DataSet: TDataSet);
    procedure kbmtbl2NewRecord(DataSet: TDataSet);
    procedure kbmtbl3BeforePost(DataSet: TDataSet);
    procedure kbmtbl3NewRecord(DataSet: TDataSet);
    procedure btnRefreshItemClick(Sender: TObject);
    procedure btnRefreshOrderClick(Sender: TObject);
    procedure btnMtDlDataClick(Sender: TObject);
    procedure btnAppUpdateClick(Sender: TObject);
  private
    { Private declarations }
  public
    DataOperateFrm: TDataOperate;
    CustNo, OrderNo: integer;
  end;

implementation

{$R *.dfm}

procedure TframRtcKbmUpdate.btnAppUpdateClick(Sender: TObject);
var
  Data: TRtcValue;
  tblName: String;
begin
  tblName := 'customer';
  Data := rtcdtstmntr1.ExtractChanges;
  if assigned(Data) then
  begin
    DataOperateFrm.UpdateData(tblName, Data, 'rtcSubmit');
  end;

  tblName := 'orders';
  Data := rtcdtstmntr2.ExtractChanges;
  if assigned(Data) then
  begin
    DataOperateFrm.UpdateData(tblName, Data, 'rtcSubmit');
  end;

  tblName := 'items';
  Data := rtcdtstmntr3.ExtractChanges;
  if assigned(Data) then
  begin
    DataOperateFrm.UpdateData(tblName, Data, 'rtcSubmit');
  end;
end;

procedure TframRtcKbmUpdate.btnMtDlDataClick(Sender: TObject);
var
   tmpData:TRtcDataSet;
begin
  { Close Orders and Items DataSets, so the user can't make changes
    to unrelated Orders or Items while waiting for new Customer data ... }
  CustNo := 0;
  kbmtbl2.Active := False;
  OrderNo := 0;
  kbmtbl3.Active := False;

  { Closing the DataSet for which new data is being requested is NOT
    necessary, but it shows the user that new data is being loaded. }
  kbmtbl1.Active := False;
  lblStatus.Caption := 'Requesting Customer data';

  tmpData:= DataOperateFrm.GetRemoteTable('customer', nil);
  rtcdtstmntr1.Active:=False;
  kbmtbl1.DisableControls;
  try
    RtcDataSetFieldsToDelphi(tmpData, kbmtbl1);
    kbmtbl1.CreateTable;
    RtcDataSetRowsToDelphi(tmpData, kbmtbl1);
  finally
    kbmtbl1.EnableControls;
    rtcdtstmntr1.Active:=True;
    kbmtbl1.First;
    end;
end;

procedure TframRtcKbmUpdate.btnRefreshItemClick(Sender: TObject);
var
  SqlFilter: TList<SqlFilterRd>;
  item: SqlFilterRd;
  tmpData:TRtcDataSet;
begin
  SqlFilter := TList<SqlFilterRd>.Create;
  try
    if OrderNo <> 0 then
    begin
      { Closing the DataSet for which new data is being requested is NOT
        necessary, but it shows the user that new data is being loaded. }
      lblStatus.Caption := 'Requesting Items data, OrderNo=' +
        IntToStr(OrderNo);

      with kbmtbl3 do
      begin
        if Active then
          Active := False;
        item.ParmType := 'eq';
        item.Parm := 'OrderNo';
        item.Value := OrderNo;
        SqlFilter.Add(item);
        tmpData := DataOperateFrm.GetRemoteTable('items', SqlFilter);

        rtcdtstmntr3.Active := False;
        DisableControls;
        try
          RtcDataSetFieldsToDelphi(tmpData, kbmtbl3);
          kbmtbl3.CreateTable;
          RtcDataSetRowsToDelphi(tmpData, kbmtbl3);
        finally
          EnableControls;
          rtcdtstmntr3.Active := True;
          First;
        end;
      end;
    end
    else
    begin
      { There is no Order info, close the Items dataset. }
      kbmtbl3.Active := False;
      lblStatus.Caption := 'No Order selected, Items table closed.';
    end;
  finally
    FreeAndNil(SqlFilter);
  end;
end;

procedure TframRtcKbmUpdate.btnRefreshOrderClick(Sender: TObject);
var
  SqlFilter: TList<SqlFilterRd>;
  item: SqlFilterRd;
  tmpData:TRtcDataSet;
begin
  SqlFilter := TList<SqlFilterRd>.Create;
  try
    { Close the Items DataSet, so the user can't make changes
      to unrelated Items while waiting for new Order data ... }
    OrderNo := 0;
    kbmtbl3.Active := False;

    if CustNo <> 0 then
    begin
      { Closing the DataSet for which new data is being requested is NOT
        necessary, but it shows the user that new data is being loaded. }
      lblStatus.Caption := 'Requesting Orders data, CustNo=' + IntToStr(CustNo);

      with kbmtbl2 do
      begin
        if Active then
          Active := False;
        item.ParmType := 'eq';
        item.Parm := 'CustNo';
        item.Value := CustNo;
        SqlFilter.Add(item);
        tmpData := DataOperateFrm.GetRemoteTable('Orders', SqlFilter);
        rtcdtstmntr2.Active := False;
        DisableControls;
        try
          RtcDataSetFieldsToDelphi(tmpData, kbmtbl2);
          kbmtbl2.CreateTable;
          RtcDataSetRowsToDelphi(tmpData, kbmtbl2);
        finally
          EnableControls;
          rtcdtstmntr2.Active := True;
          First;
        end;
      end;

    end
    else
    begin
      { There is no Customer info, close the Orders dataset. }
      kbmtbl2.Active := False;
      lblStatus.Caption := 'No Customer selected, Orders table closed.';
    end;
  finally
    FreeAndNil(SqlFilter);
  end;
end;

procedure TframRtcKbmUpdate.kbmtbl1AfterScroll(DataSet: TDataSet);
var
  NewNo: integer;
begin
  { Customer data received, or the user has scrolled to a new Customer row.
    For our Master/Detail relation with Orders to work,
    we need to refresh our "Orders" data with a new "CustNo". }

  if kbmtbl1.Active and (kbmtbl1.RecordCount > 0) then
    NewNo := kbmtbl1.FieldByName('CustNo').AsInteger
  else
    NewNo := 0;

  if CustNo <> NewNo then
  begin
    CustNo := NewNo;
    btnRefreshOrderClick(nil);
  end;

end;

procedure TframRtcKbmUpdate.kbmtbl1BeforeDelete(DataSet: TDataSet);
begin
  if not chkValidCheck.Checked then
    Exit;

  { We can leave this job to the Server, but then the user will see
    the record deleted locally and then get an error from the Server. }
  if kbmtbl2.RecordCount > 0 then
    raise Exception.Create
      ('Can not delete Customer before deleting all Customer Orders.');
end;

procedure TframRtcKbmUpdate.kbmtbl1BeforePost(DataSet: TDataSet);
begin
  if not chkValidCheck.Checked then
    Exit;

  { We can leave this job to the Server, but then the user will
    have to refresh the dataset and repeat the last operation in
    case a field was undefined, so we will make this check here. }
  if kbmtbl1.FieldByName('CustNo').IsNull then
    raise Exception.Create('Required field "CustNo" is NULL');
end;

procedure TframRtcKbmUpdate.kbmtbl2AfterScroll(DataSet: TDataSet);
var
  NewNo: integer;
begin
  { Orders data received, or the user has scrolled to a new Order row.
    For our Master/Detail relation with Items to work,
    we need to refresh our "Items" data with a new "OrderNo". }

  if kbmtbl2.Active and (kbmtbl2.RecordCount > 0) then
    NewNo := kbmtbl2.FieldByName('OrderNo').AsInteger
  else
    NewNo := 0;

  if OrderNo <> NewNo then
  begin
    OrderNo := NewNo;
    btnRefreshItemClick(nil);
  end;

end;

procedure TframRtcKbmUpdate.kbmtbl2BeforeDelete(DataSet: TDataSet);
begin
  if not chkValidCheck.Checked then
    Exit;

  { We can leave this job to the Server, but then the user will see
    the record deleted locally and then get an error from the Server. }
  if kbmtbl3.RecordCount > 0 then
    raise Exception.Create
      ('Can not delete Order before deleting all Order Items.');
end;

procedure TframRtcKbmUpdate.kbmtbl2BeforePost(DataSet: TDataSet);
begin
  if not chkValidCheck.Checked then
    Exit;

  { We can leave this job to the Server, but then the user will
    need to refresh the dataset and repeat the last operation in
    case a field is undefined, so we will make this check here. }
  if kbmtbl2.FieldByName('CustNo').AsInteger <> CustNo then
    raise Exception.Create('Required field "CustNo" has invalid value')
  else if kbmtbl2.FieldByName('OrderNo').IsNull then
    raise Exception.Create('Required field "OrderNo" is NULL')
  else if kbmtbl2.FieldByName('EmpNo').IsNull then
    raise Exception.Create('Required field "EmpNo" is NULL');
end;

procedure TframRtcKbmUpdate.kbmtbl2NewRecord(DataSet: TDataSet);
begin
  { We should set all "master" fields for a new Order here }
  kbmtbl2.FieldByName('CustNo').AsInteger := CustNo;
end;

procedure TframRtcKbmUpdate.kbmtbl3BeforePost(DataSet: TDataSet);
begin
  if not chkValidCheck.Checked then
    Exit;

  { We can leave this job to the Server, but then the user will
    need to refresh the dataset and repeat the last operation in
    case a field is undefined, so we will make this check here. }
  if kbmtbl3.FieldByName('OrderNo').AsInteger <> OrderNo then
    raise Exception.Create('Required field "OrderNo" has invalid value')
  else if kbmtbl3.FieldByName('ItemNo').IsNull then
    raise Exception.Create('Required field "ItemNo" is NULL');
end;

procedure TframRtcKbmUpdate.kbmtbl3NewRecord(DataSet: TDataSet);
begin
  { We should set all "master" fields for a new Item here }
  kbmtbl3.FieldByName('OrderNo').AsInteger := OrderNo;
end;

procedure TframRtcKbmUpdate.RtcMemDataSetDataChange(Sender: TObject);
var
  Data: TRtcValue;
  tblName: String;
begin
  if chbQAppUpdate.Checked then
    Exit;

  { We are using a single "OnDataChange" implementation for all DataSets }
  if Sender = rtcdtstmntr1 then
  begin
    tblName := 'customer';
    // Refresh Orders if Customer field value has changed
    kbmtbl1AfterScroll(nil);
  end
  else if Sender = rtcdtstmntr2 then
  begin
    tblName := 'orders';
    // Refresh Items if Order field value was changed
    kbmtbl2AfterScroll(nil);
  end
  else if Sender = rtcdtstmntr3 then
    tblName := 'items';

  Data := TRtcDataSetMonitor(Sender).ExtractChanges;
  if assigned(Data) then
  begin
    lblStatus.Caption := 'Sending ' + tblName + ' changes to the Server ...';
    DataOperateFrm.UpdateData(tblName, Data, 'rtcSubmit');
  end;
end;

end.
